package com.hframe.Json;


import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.hframe.data.Base;
import com.hframe.data.DRef;
import com.hframe.type.*;
import com.hframe.typefilter.FilterRef;
import com.hframe.typefilter.StringToDate;
import org.apache.log4j.Logger;

import java.lang.reflect.Field;
import java.util.Map;

/**
 * Json 转换成对象
 */
public class JsonToData<T extends Base> implements FilterRef<T, Object> {
    private static Logger log = Logger.getLogger(JsonToData.class);


    @Override
    public T filter(Object object) throws Exception {

        return null;
    }

    protected static boolean convertArray(JsonElement jsEl, TArray array) {
        if (null == jsEl || null == array)
            return false;
        try {
            if (jsEl.isJsonArray()) {
                JsonArray arr = jsEl.getAsJsonArray();
                for (int i = 0; i < arr.size(); i++) {
                    Base object = (Base) array.getClassName().newInstance();
                    convert(arr.get(i), object);
                    array.add(object);
                }
                return true;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    protected static boolean convertData(JsonElement jsEl, TData data) {
        if (null == jsEl || null == data)
            return false;
        try {
            if (jsEl.isJsonObject()) {
                Object object = data.get();
                if (null == object) {
                    if (null == data.getClassName())
                        return false;

                    object = data.getClassName().newInstance();
                    data.set(object);
                }

                return convert(jsEl, (Base) object);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }


    public static boolean convert(JsonElement jsEl, Base data, String... keys) throws Exception {
        if (null == jsEl || null == data)
            return false;

        if (jsEl.isJsonObject() && data instanceof DRef) {
            for (Map.Entry<String, JsonElement> pro : jsEl.getAsJsonObject().entrySet()) {
                convert(pro.getValue(), (DRef<?>) data, pro.getKey(), keys);
            }
            return true;

        } else if (data instanceof TData) {
            return convertData(jsEl, (TData) data);

        } else if (data instanceof TArray) {
            return convertArray(jsEl, (TArray) data);

        } else if (data instanceof com.hframe.type.T) {
            JsonPrimitive value;
            if (jsEl.isJsonNull()) {
                value = null;
            } else if (jsEl.isJsonPrimitive()) {
                value =  jsEl.getAsJsonPrimitive();
            }else {
                value = new JsonPrimitive(jsEl.toString());
            }
            return setDataValue((com.hframe.type.T<?>) data, value);
        }

        return false;
    }

    protected static boolean convert(JsonElement jsEl, DRef<?> data, String key, String... keys) {
        if (null == jsEl || null == data)
            return false;

        try {
            com.hframe.type.T<?> object = findField(data, key, keys);
            if (null != object) {
                if (jsEl.isJsonPrimitive()) {
                    return setDataValue(object, jsEl.getAsJsonPrimitive());

                } else {
                    convert(jsEl, object);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
        return true;
    }


    protected static com.hframe.type.T<?> findField(DRef<?> data, String key, String... keys) {
        if (null == key || 0 >= key.trim().length())
            return null;

        Class<?> cla = data.getClass();
        while (null != cla && DRef.class != cla) {
            for (Field field : cla.getDeclaredFields()) {
                field.setAccessible(true);
                try {
                    if (!isExtendsClass(field.getType(), com.hframe.type.T.class))
                        continue;

                    String fieldName = field.getName();
                    if (keys.length > 0 && !isOut(fieldName, keys))
                        return null;

                    Object object = field.get(data);

                    if (null == object)
                        continue;

                    com.hframe.type.T<?> typeObj = (com.hframe.type.T<?>) object;
                    JsonFiletr filter = typeObj.getfilter(JsonFiletr.class);
                    if (null != filter) {
                        if (filter.getReadWait() == JsonFiletr.ReadWait.OnlyRead || filter.getReadWait() == JsonFiletr.ReadWait.NotReadWait)
                            return null;

                        fieldName = filter.getName();
                    }

                    if (null == fieldName)
                        fieldName = field.getName();

                    if (key.toLowerCase().equals(fieldName.toLowerCase())) {
                        return typeObj;
                    }

                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            cla = cla.getSuperclass();
        }
        return null;
    }

    private static boolean isOut(String name, String... keys) {
        for (String key : keys) {
            if (key.toLowerCase().equals(name.toLowerCase()))
                return true;
        }
        return false;
    }

    protected static boolean setDataValue(com.hframe.type.T<?> typeObj, JsonPrimitive val) throws Exception {
        if (null == val || null == typeObj)
            return false;

        JsonFiletr filter = typeObj.getfilter(JsonFiletr.class);
        if (null != filter) {
            if (val.isString())
                filter.set(val.getAsString());
            else
                filter.set(val.toString());
        } else {
            if (typeObj instanceof TShort) {
                ((TShort) typeObj).set(val.getAsShort());

            } else if (typeObj instanceof TLong) {
                ((TLong) typeObj).set(val.getAsLong());

            } else if (typeObj instanceof TInt) {
                ((TInt) typeObj).set(val.getAsInt());

            } else if (typeObj instanceof TFloat) {
                ((TFloat) typeObj).set(val.getAsFloat());

            } else if (typeObj instanceof TDouble) {
                ((TDouble) typeObj).set(val.getAsDouble());

            } else if (typeObj instanceof TChar) {
                ((TChar) typeObj).set(val.getAsCharacter());

            } else if (typeObj instanceof TByte) {
                ((TByte) typeObj).set(val.getAsByte());

            } else if (typeObj instanceof TBoolean) {
                ((TBoolean) typeObj).set(val.getAsBoolean());

            } else if (typeObj instanceof TDate) {
                ((TDate) typeObj).set(StringToDate.parse(val.getAsString(), StringToDate.DATE_TIME_LINE));

            } else if (typeObj instanceof TString) {
                ((TString) typeObj).set(val.getAsString());

            } else if (typeObj instanceof TBigDecimal) {
                ((TBigDecimal) typeObj).set(val.getAsBigDecimal());

            } else if (typeObj instanceof TBigInteger) {
                ((TBigInteger) typeObj).set(val.getAsBigInteger());

            } else if (typeObj instanceof TEnum) {
                ((TEnum) typeObj).set(getEnumValue(typeObj, val.getAsInt()));

            } else {
                return false;
            }
        }
        return true;
    }

    private static Enum getEnumValue(com.hframe.type.T<?> typeObj, Integer val) throws IllegalAccessException {
        if (null == val)
            return null;

        Class<Enum> className = (Class<Enum>) typeObj.getClassName();
        if (null != className) {
            Field[] fields = className.getDeclaredFields();
            for (Field field : fields) {
                if (!field.isEnumConstant())
                    continue;

                Object object = field.get(null);
                if (((Enum) object).ordinal() == val) {
                    return (Enum) object;
                }
            }
        }
        return null;
    }

    protected static boolean isExtendsClass(Class<?> src, Class<?> dis) {
        while (src != null) {
            if (src == dis)
                return true;
            src = src.getSuperclass();
        }
        return false;
    }

}